﻿using FFmpeg.AutoGen;
using System;

namespace FFmpegLib
{
    public sealed unsafe class FAudioFifo : IDisposable
    {
        public AVAudioFifo* Fifo;

        public AVSampleFormat Fmt { get; private set; }
        public int Channels { get; private set; }
        public uint SampleSize { get; private set; }
        public int PerSampleSize { get; private set; }

        public FAudioFifo(AVSampleFormat fmt = AVSampleFormat.AV_SAMPLE_FMT_S16, int channels = 2, uint sampleSize = 100000)
        {
            Channels = channels;
            SampleSize = sampleSize;
            Fifo = ffmpeg.av_audio_fifo_alloc(fmt, channels, (int)sampleSize);
            PerSampleSize = ffmpeg.av_get_bytes_per_sample(fmt);
        }

        public int Size()
        {
            if (disposedValue) { return 0; }
            return ffmpeg.av_audio_fifo_size(Fifo);
        }

        public bool Enqueue(void* data, int sampleNum)
        {
            lock (this)
            {
                int freeSize = ffmpeg.av_audio_fifo_space(Fifo);
                if (sampleNum > freeSize)
                    return false;

                int writedNum = ffmpeg.av_audio_fifo_write(Fifo, &data, sampleNum);
                return writedNum == sampleNum;
            }
        }

        public void* Dequeue(int readSampleNum, out int dataSize)
        {
            void* data = null;
            dataSize = 0;
            lock (this)
            {
                int toReadNum = ffmpeg.av_audio_fifo_size(Fifo);
                if (toReadNum < 1) { return null; }

                if (readSampleNum > toReadNum)
                    readSampleNum = toReadNum;

                dataSize = Channels * readSampleNum * PerSampleSize;
                IntPtr buf = System.Runtime.InteropServices.Marshal.AllocHGlobal(dataSize);
                data = buf.ToPointer();
                int writedNum = ffmpeg.av_audio_fifo_read(Fifo, &data, readSampleNum);
            }
            return data;
        }

        public void Clear()
        {
            lock (this)
            {
                int sampleNum = ffmpeg.av_audio_fifo_size(Fifo);
                ffmpeg.av_audio_fifo_drain(Fifo, sampleNum);
            }
        }

        #region dispose
        private bool disposedValue;
        protected void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    if (Fifo != null)
                    {
                        Clear();
                        ffmpeg.av_audio_fifo_free(Fifo);
                        Fifo = null;
                    }
                }
                disposedValue = true;
            }
        }

        public void Dispose()
        {
            // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }
        #endregion

    }
}